using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using XNAProject.PathFinder;
using XNAProject.Core;
using XNAProject.Enumerations;


namespace XNAProject.GameElements
{
    public class Ship : Microsoft.Xna.Framework.DrawableGameComponent                               //basic ship class
    {                                                                                               //will be the parent of all
        #region Fields

        #region Graphics

        protected Model model;                                                                      //props
        protected Effect effect;

        protected Texture2D colorMap;
        protected Texture2D normalMap;
        protected Matrix[] modelBones;

        protected float Scale { get; set; }
        protected Vector3[] rotationContainer;
        protected Vector3 Rotation;
        protected Vector3 Translation { get; set; }
        protected Vector3 graphicTranslation;
        protected float Hue { get; set; }
        protected Vector4 vLightDirection { get; set; }

        protected bool moveUp;
        protected Vector2 moveUpBorder;

        #endregion 

        public Vector2 hexaPosition { get; set; }
        public Vector2 moveTarget { get; set; }
        public List<Vector2> path;
        protected Vector3 position;

        public string name { get; set; }

        public int defense { get; set; }
        public int offense { get; set; }
        public int movement { get; set; }
        public int cost { get; set; }
        public int health { get; set; }
        public int oil { get; set; }
        public int consumption { get; set; }
        public int sight { get; set; }
        public int PathMovesNeeded { get; set; }

        protected bool selfRepaire;
        protected bool isAlive;
        protected bool wreck;
        public bool visible { get; set; }
        protected bool pathReady;
        protected bool pathConfirmed;

        protected Level level;
        protected Player player;
        protected IPathFinder PathFinder;

        public bool selected { get; set; }

        #endregion

        #region BasicProcedures

        public Ship(Game game, Vector2 HexaPosition): base(game)
        {
            player = (Player)Game.Services.GetService(typeof(Player));
            level = (Level)Game.Services.GetService(typeof(Level));
            PathFinder = (IPathFinder)Game.Services.GetService(typeof(IPathFinder));

            hexaPosition = moveTarget = HexaPosition;

            level.hexagonCollection[(int)hexaPosition.X, (int)hexaPosition.Y].ship = this;

            visible = true;
            moveUp = false;
            pathReady = false;
            pathConfirmed = false;
            PathMovesNeeded = 0;           

            rotationContainer = new Vector3[8];
        }

        public override void Initialize()
        {
            SetSight();
            base.Initialize();
        }

        public override void Update(GameTime gameTime)
        {
            if (hexaPosition != moveTarget && pathConfirmed)
            {
                MoveProcess();
            }
            if (level[hexaPosition].visible)
                visible = true;
            else visible = false;
 
            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            XNAProjectCamera camera = (XNAProjectCamera)Game.Services.GetService(typeof(XNAProjectCamera));
            //draw ship
            #region OldDraw, Effect Shit
            //#region Model

            //effect.CurrentTechnique = effect.Techniques["NormalMapping"];

            //effect.Begin();

            //if (Translation.Y >= -5) moveUp = false;
            //else if (Translation.Y <= -10) moveUp = true;

            //if (moveUp)
            //    Translation = new Vector3(Translation.X, Translation.Y + 0.3f, Translation.Z);
            //else Translation = new Vector3(Translation.X, Translation.Y - 0.3f, Translation.Z);

            //Matrix renderMatrix = Matrix.CreateScale(Scale) * Matrix.CreateRotationX(Rotation.X) * Matrix.CreateRotationY(Rotation.Y)
            //        * Matrix.CreateRotationZ(Rotation.Z) * Matrix.CreateTranslation(Translation);

            //foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            //{
            //    pass.Begin();

            //    foreach (ModelMesh mesh in model.Meshes)
            //    {
            //        foreach (ModelMeshPart part in mesh.MeshParts)
            //        {
            //            Matrix worldMatrix = modelBones[mesh.ParentBone.Index] * renderMatrix;

            //            Vector4 vecEye = new Vector4(camera.Position.X, camera.Position.Y, camera.Position.Z, 0);

            //            effect.Parameters["matWorldViewProj"].SetValue(worldMatrix * camera.View * camera.Projection);
            //            effect.Parameters["matWorld"].SetValue(worldMatrix);
            //            effect.Parameters["vecEye"].SetValue(vecEye);
            //            effect.Parameters["vecLightDir"].SetValue(vLightDirection);
            //            effect.Parameters["ColorMap"].SetValue(colorMap);
            //            effect.Parameters["NormalMap"].SetValue(normalMap);
            //            effect.Parameters["A"].SetValue(Hue);

            //            GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, part.StreamOffset, part.VertexStride);
            //            GraphicsDevice.Indices = mesh.IndexBuffer;
            //            GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,
            //                                                          part.BaseVertex, 0, part.NumVertices,
            //                                                          part.StartIndex, part.PrimitiveCount);
            //        }
            //    }
            //    pass.End();
            //}
            //effect.End();

            //#endregion 

            #endregion
            if (visible)
            {
                if (Translation.Y >= moveUpBorder.X) moveUp = false;
                else if (Translation.Y <= moveUpBorder.Y) moveUp = true;

                if (moveUp)
                    Translation = new Vector3(Translation.X, Translation.Y + 0.3f, Translation.Z);
                else Translation = new Vector3(Translation.X, Translation.Y - 0.3f, Translation.Z);

                foreach (ModelMesh mesh in model.Meshes)
                {
                    foreach (BasicEffect effect in mesh.Effects)
                    {
                        effect.EnableDefaultLighting();
                        effect.PreferPerPixelLighting = true;

                        effect.World =
                            Matrix.CreateFromYawPitchRoll(Rotation.X, Rotation.Y, Rotation.Z) *
                            Matrix.CreateScale(Scale) *
                            Matrix.CreateTranslation(Translation);

                        effect.Projection = camera.Projection;
                        effect.View = camera.View;
                    }
                    mesh.Draw();
                }
            }
            base.Draw(gameTime);
        }

        #endregion

        #region Procedures

        protected void SetTranslation(Vector3 graphicTranslation)
        {
            this.graphicTranslation = graphicTranslation;

            Translation = new Vector3(level[hexaPosition].Translation.X + graphicTranslation.X,
                    level[hexaPosition].Translation.Y + graphicTranslation.Y,
                    level[hexaPosition].Translation.Z + graphicTranslation.Z);
        }

        protected void SetFaceing(Vector3 Up, Vector3 UpRight, Vector3 Right, Vector3 RightDown
            , Vector3 Down, Vector3 DownLeft, Vector3 Left, Vector3 LeftUp)
        {
            rotationContainer[0] = Up;
            rotationContainer[1] = UpRight;
            rotationContainer[2] = Right;
            rotationContainer[3] = RightDown;
            rotationContainer[4] = Down;
            rotationContainer[5] = DownLeft;
            rotationContainer[6] = Left;
            rotationContainer[7] = LeftUp;
        }

        public void Face(ShipFaceing faceing)
        {
            switch (faceing)
            {
                case ShipFaceing.Up:
                    Rotation = rotationContainer[0];
                    break;
                case ShipFaceing.UpRight:
                    Rotation = rotationContainer[1];
                    break;
                case ShipFaceing.Right:
                    Rotation = rotationContainer[2];
                    break;
                case ShipFaceing.RightDown:
                    Rotation = rotationContainer[3];
                    break;
                case ShipFaceing.Down:
                    Rotation = rotationContainer[4];
                    break;
                case ShipFaceing.LeftDown:
                    Rotation = rotationContainer[5];
                    break;
                case ShipFaceing.Left:
                    Rotation = rotationContainer[6];
                    break;
                case ShipFaceing.LeftUp:
                    Rotation = rotationContainer[7];
                    break;
                default:
                    break;
            }
        }

        public void SetSight()
        //set visible hexagons
        {
            if (visible)
            {
                List<Vector2> sightAreaOfSelf = level[hexaPosition].GetAreaAroundSelf(sight);
                //get the area in sight around the ship

                foreach (Vector2 hexpos in sightAreaOfSelf)
                {
                    level[hexpos].visible = true;                    
                    level[hexpos].discovered = true;
                }
            }
        }

        public void UndoSight()
        //set not visible hexagons
        {
            if (visible)
            {
                List<Vector2> sightAreaOfSelf = level[hexaPosition].GetAreaAroundSelf(sight);
                //get the area in sight around the ship

                foreach (Vector2 hexpos in sightAreaOfSelf)
                {
                    level[hexpos].visible = false;                    
                }
            }
        }

#region Move

        public void Move(Vector2 Position)
        {
            moveTarget = Position;
            PathFinder.RegisterPathFindingRequest(this);        
        }

        private void MoveThere(Vector2 Position, ShipFaceing faceing)
        {
            if (hexaPosition != moveTarget)
            {
                UndoSight();
                level[hexaPosition].ship = null;
                level[hexaPosition].UnSelect();

                hexaPosition = Position;

                level[hexaPosition].ship = this;
                level[hexaPosition].Select();
                level.selected = hexaPosition;

                Translation = new Vector3(level[hexaPosition].Translation.X + graphicTranslation.X,
                    level[hexaPosition].Translation.Y + graphicTranslation.Y,
                    level[hexaPosition].Translation.Z + graphicTranslation.Z);

                Face(faceing);

                foreach (Ship ship in player)
                {
                    ship.SetSight();
                }

                if (hexaPosition == moveTarget) //ship arrived
                {
                    level[hexaPosition].UnTarget();
                    level[hexaPosition].Select();
                    level.selected = hexaPosition;
                }
            }
        }

        public void PathDone(List<Vector2> Path)
        {
            path = Path;
            level.EndWaypoint();
            if (path != null)
            {
                PathMovesNeeded = path.Count - 1;
                pathReady = true;
                level.DrawWaypoint(path);
            }
            else
            {
                ConsoleMenu console = (ConsoleMenu)Game.Services.GetService(typeof(ConsoleMenu));
                console.ActionText("Can't move There!");
            }
        }

        public void ActuallyMove()
        {
            if (path != null)
                pathConfirmed = true;
        }

        public void MoveProcess()
        {
            Vector2 temp = path[path.Count - PathMovesNeeded];

            if (hexaPosition.X > temp.X && hexaPosition.Y > temp.Y)
                MoveThere(temp, ShipFaceing.LeftUp);
            else if (hexaPosition.X < temp.X && hexaPosition.Y < temp.Y)
                MoveThere(temp, ShipFaceing.RightDown);
            else if (hexaPosition.X > temp.X && hexaPosition.Y < temp.Y)
                MoveThere(temp, ShipFaceing.LeftDown);
            else if (hexaPosition.X < temp.X && hexaPosition.Y > temp.Y)
                MoveThere(temp, ShipFaceing.UpRight);
            else if (hexaPosition.X > temp.X && hexaPosition.Y == temp.Y)
                MoveThere(temp, ShipFaceing.Left);
            else if (hexaPosition.X < temp.X && hexaPosition.Y == temp.Y)
                MoveThere(temp, ShipFaceing.Right);
            else if (hexaPosition.X == temp.X && hexaPosition.Y > temp.Y)
                MoveThere(temp, ShipFaceing.Up);
            else if (hexaPosition.X == temp.X && hexaPosition.Y < temp.Y)
                MoveThere(temp, ShipFaceing.Down);

            PathMovesNeeded--;
            if (hexaPosition == moveTarget && PathMovesNeeded == 0)
            {
                level.EndWaypoint();
                level.Select(hexaPosition);
                pathConfirmed = false;
                pathReady = false;
                path = null;
                
            }
        }

        private bool MoveUp()
        {
            if (hexaPosition.Y - 1 >= 0 && hexaPosition.Y > moveTarget.Y
                    && level[hexaPosition - Vector2.UnitY].ship == null)
            {
                MoveThere(hexaPosition - Vector2.UnitY, ShipFaceing.Up);
                return true;
            }
            return false;            
        }

        private bool MoveUpRight()
        {
            if (hexaPosition.X + 1 < level.size.X && hexaPosition.X > moveTarget.X
                    && hexaPosition.Y - 1 >= 0 && hexaPosition.Y > moveTarget.Y
                    && level[hexaPosition + Vector2.UnitX - Vector2.UnitY].ship == null)
            {
                MoveThere(hexaPosition + Vector2.UnitX - Vector2.UnitY, ShipFaceing.UpRight);
                return true;
            }
            return false;
        }

        private bool MoveRight()
        {
            if (hexaPosition.X + 1 < level.size.X && hexaPosition.X < moveTarget.X
                    && level.hexagonCollection[(int)hexaPosition.X + 1, (int)hexaPosition.Y].ship == null)
            {
                MoveThere(new Vector2(hexaPosition.X + 1, hexaPosition.Y), ShipFaceing.Right);
                return true;
            }
            return false;
        }

        private bool MoveRightDown()
        {
            if (hexaPosition.X + 1 < level.size.X && hexaPosition.X < moveTarget.X
                    && hexaPosition.Y + 1 < level.size.Y && hexaPosition.Y < moveTarget.Y
                    && level.hexagonCollection[(int)hexaPosition.X + 1, (int)hexaPosition.Y + 1].ship == null)
            {
                MoveThere(new Vector2(hexaPosition.X + 1, hexaPosition.Y + 1), ShipFaceing.RightDown);
                return true;
            }
            return false;
        }

        private bool MoveDown()
        {
            if (hexaPosition.Y + 1 < level.size.Y && hexaPosition.Y < moveTarget.Y
                    && level.hexagonCollection[(int)hexaPosition.X, (int)hexaPosition.Y + 1].ship == null)
            {
                MoveThere(new Vector2(hexaPosition.X, hexaPosition.Y + 1), ShipFaceing.Down);
                return true;
            }
            return false;
        }

        private bool MoveDownLeft()
        {
            if (hexaPosition.X - 1 >= 0 && hexaPosition.X > moveTarget.X
                    && hexaPosition.Y + 1 < level.size.Y && hexaPosition.Y > moveTarget.Y
                    && level.hexagonCollection[(int)hexaPosition.X - 1, (int)hexaPosition.Y + 1].ship == null)
            {
                MoveThere(new Vector2(hexaPosition.X - 1, hexaPosition.Y + 1), ShipFaceing.LeftDown);
                return true;
            }
            return false;
        }

        private bool MoveLeft()
        {
            if (hexaPosition.X - 1 >= 0 && hexaPosition.X > moveTarget.X
                    && level.hexagonCollection[(int)hexaPosition.X - 1, (int)hexaPosition.Y].ship == null)
            {
                MoveThere(new Vector2(hexaPosition.X - 1, hexaPosition.Y), ShipFaceing.Left);
                return true;
            }
            return false;
        }

        private bool MoveLeftUp()
        {
            if (hexaPosition.X - 1 >= 0 && hexaPosition.X > moveTarget.X
                    && hexaPosition.Y - 1 >= 0 && hexaPosition.Y > moveTarget.Y
                    && level.hexagonCollection[(int)hexaPosition.X - 1, (int)hexaPosition.Y - 1].ship == null)
            {
                MoveThere(new Vector2(hexaPosition.X - 1, hexaPosition.Y - 1), ShipFaceing.LeftUp);
                return true;
            }
            return false;
        }

#endregion

        public void Attack(Vector2 attackPosition)
        {
            ConsoleMenu console = (ConsoleMenu)Game.Services.GetService(typeof(ConsoleMenu));
            
            console.ActionText("Attacked " + level.hexagonCollection[(int)attackPosition.X, (int)attackPosition.Y].ship.name);
        }

        public void Select()
        {
            selected = true;
        }

        public void UnSelect()
        {
            selected = false;
        }

        public void TakeDamage(int Offense)
        {
        }

        public void Repaire(int Repaire)
        {
        }

        public void SelfRepaire(int Repaire)
        {
        }

        #endregion

        }
    }
